/* 
 *  Copyright (C) 2006 James Young
 * `gurumind(a)gmail.com
 * 
 * updated by Nick.xf 2010.01.12
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using System.IO;
using System.Linq;
using System.Globalization;

namespace IPDEditor
{
    public class Format
    {
        private string _xmlFile;
        private XElement _eleFile;

        public SortedList<string, fmtrecord> records;

        public Format()
        {
            records = new SortedList<string, fmtrecord>();

            _xmlFile = "format.xml";
            string path = Environment.CommandLine.Substring(0, Environment.CommandLine.LastIndexOf("\\")).Replace("\"", "") + "\\" + _xmlFile;
            if (File.Exists(path))
            {
                _eleFile = XElement.Load(path);

                //load formats
                foreach (XElement ele in _eleFile.Elements())
                {
                    if (ele.Name == "ipdformat")
                    {
                        foreach (XElement xrec in ele.Elements())
                        {
                            fmtrecord rec = new fmtrecord();
                            rec.name = xrec.Attribute("name").Value;
                            rec.version = EncUtil.String2Int(xrec.Attribute("version").Value);
                            records.Add(rec.ToString(), rec);
                            foreach (XElement xfld in xrec.Elements())
                            {
                                string fmtStr = xfld.Attribute("format").Value;
                                fmtfield fld = fmtfield.Create(fmtStr);
                                fld.name = xfld.Attribute("name").Value;
                                fld.type = EncUtil.String2Int(xfld.Attribute("type").Value);

                                if (xfld.Attributes("offset").Count() > 0)
                                    fld.offset = EncUtil.String2Int(xfld.Attribute("offset").Value);
                                if (xfld.Attributes("length").Count() > 0)
                                    fld.length = EncUtil.String2Int(xfld.Attribute("length").Value);
                                if (xfld.Attributes("index").Count() > 0)
                                    fld.index = EncUtil.String2Int(xfld.Attribute("index").Value);

                                fld.Extra = xfld.Value;

                                rec.fields.Add(fld);
                            }
                        }
                    }
                }
            }
        }
    }

    public abstract class fmtfield
    {
        public string name;
        public int type;
        public int index;
        public string format;

        public int offset = 0;
        public int length = 0;
        private string extra;
        protected char[] sep = { '|' };

        public virtual string Extra
        {
            get { return extra; }
            set { extra = value; }
        }

        public virtual string ReadBinary(byte[] data)
        {
            throw new Exception("Not implimented");
        }

        public virtual byte[] WriteBinary()
        {
            throw new Exception("Not implimented");
        }

        public static fmtfield Create(string format)
        {
            switch (format)
            {
                case "ascii":
                    return new fmtAsciiField();
                case "unicode":
                    return new fmtUnicodeField();
                case "lzunicode":
                    return new fmtLzUnicodeField();
                case "datetime":
                    return new fmtDatetimeField();
                case "bool":
                    return new fmtBoolField();
                case "enum":
                    return new fmtEnumField();
                case "utf8":
                    return new fmtUTF8Field();
                case "int":
                    return new fmtIntField();
                default:
                    throw new Exception("Invalid format of field: " + format);
            }
        }

        protected byte[] GetDataBuffer(byte[] data, int getOffset, int getLength)
        {
            byte[] buff = data;
            if (data.Length > 0 && getOffset > 0 && getLength < data.Length - getOffset)
            {
                int len = getLength == 0 ? data.Length - getOffset : getLength;
                buff = new byte[len];
                for (int i = 0; i < len; i++)
                    buff[i] = data[i + getOffset];
            }
            return buff;
        }

        protected byte[] GetDataBuffer(byte[] data)
        {
            if (data == null)
            {
                return null;
            }

            return GetDataBuffer(data, offset, length);
        }
    }

    public class fmtAsciiField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            if (data == null)
            {
                return null;
            }
            return EncUtil.BIN2AsciiOnly(GetDataBuffer(data));
        }
    }

    public class fmtUnicodeField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            return EncUtil.UCS2String(GetDataBuffer(data));
        }
    }


    public class fmtLzUnicodeField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            if (data == null)
            {
                return "";
            }
            return EncUtil.UCS2String(GetDataBuffer(data, offset + 1, length));
        }
    }

    public class fmtBoolField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            char sep = '|';
            string[] ss = Extra.Split(sep);
            if (data[0] == 0)
                return ss[0];
            else
                return ss[1];
        }
    }

    public class fmtIntField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {            
            //return Convert.ToInt32(data[3]<<24+data[2]<<16+data[1]<<8+data[0]).ToString();
            UInt32 duration = BitConverter.ToUInt32(data, 0);
            uint Mins = Convert.ToUInt32(duration/60);            
            uint Secs = duration % 60;

            return Mins.ToString() + ":" + Secs.ToString();
        }
    }

    public class fmtDatetimeField : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            DateTime javaEpoch = new DateTime(1970, 1, 1);
            Int64 milliseconds = EncUtil.BIN2Long(GetDataBuffer(data));
            DateTime dotNetDateTime = javaEpoch.AddMilliseconds(milliseconds);
            return dotNetDateTime.ToString("g");
        }
    }

    public class fmtEnumField : fmtfield
    {
        private SortedList<int, string> list;

        public fmtEnumField()
        {

        }

        public override string Extra
        {
            get
            {
                return base.Extra;
            }
            set
            {
                base.Extra = value;
                list = new SortedList<int, string>();
                
                StringReader sr = new StringReader(Extra);
                string line = sr.ReadLine();
                while (line != null)
                {
                    if (line.Trim() != string.Empty)
                    {
                        string[] ss = line.Split(sep);
                        list.Add(EncUtil.String2Int(ss[0]), ss[1].Trim());
                    }

                    line = sr.ReadLine();
                }
            }
        }
        public override string ReadBinary(byte[] data)
        {
            int val = EncUtil.BIN2Int(data);
            if (list.ContainsKey(val))
                return list[val];
            else
                return "unkown";
        }
    }

    public class fmtUTF8Field : fmtfield
    {
        public override string ReadBinary(byte[] data)
        {
            if (data == null)
            {
                return "";
            }
            return EncUtil.UTF8String(GetDataBuffer(data, offset + 1, length));
        }
    }
    public class fmtrecord
    {
        public List<fmtfield> fields;
        public string name;
        public int version;

        public static string format = "{0} (ver {1})";
        public fmtrecord()
        {
            fields = new List<fmtfield>();
        }
        public override string ToString()
        {
            return string.Format(format, name, version);
        }
    }

}
